// base64.cpp
#include "base64.h"
#include "stdio.h"
#include "stdlib.h"

static const char gs_szBin2Ascii[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static const U8 gs_u8aAscii2Bin[128] = { // 0xE0,0xF0是旧版本的错误码，已不再使用。大于等于0x80都视为非法数据。
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xF0, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xF2, 0xFF, 0x3F, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF,
    0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

int BASE64_Encode(const STD_BUFFER& bufIn, string& strOut)
{
    int iRet = -1;
    int iLen = bufIn.size() * 4 / 3 + 4;
    STD_BUFFER bufOut;

    bufOut.resize(iLen);
    iRet = BASE64_Encode(bufIn.data(), bufIn.size(), (char*)bufOut.data(), iLen);
    if(iRet >= 0) strOut = string((char*)bufOut.data(), iLen);

    return iRet;
}

int BASE64_Decode(const string& strIn, STD_BUFFER& bufOut)
{
    int iRet = -1;
    int iLen = strIn.length() * 3 / 4 + 4;

    bufOut.resize(iLen);
    iRet = BASE64_Decode(strIn.c_str(), strIn.length(), bufOut.data(), iLen);
    if(iRet >= 0) bufOut.resize(iLen);

    return iRet;
}

int BASE64_Encode(IN const BYTE* data, IN int iLenIn, OUT char* pcOut, int& iLenOut)
{
    int i, iRet = -1;
    U8 u8_BytesArry[3];
    char* pcWrite = pcOut;

    if(data == NULL || iLenIn <= 0 || pcOut == NULL || iLenOut <= 0) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);

    i = ((iLenIn + 2) / 3) * 4;
    if(iLenOut >= i) iLenOut = i;
    else SET_RET_AND_GOTO_EXIT(COMMON_ERR_NOMEM);

    for(i = 0; i < iLenIn; i += 3)
    {
        if(iLenIn - i >= 3)
        { // 将3字节数据转换成4个ASCII字符
            u8_BytesArry[0] = data[i];
            u8_BytesArry[1] = data[i + 1];
            u8_BytesArry[2] = data[i + 2];

            *pcWrite++ = gs_szBin2Ascii[u8_BytesArry[0] >> 2];
            *pcWrite++ = gs_szBin2Ascii[((u8_BytesArry[0] << 4) | (u8_BytesArry[1] >> 4)) & 0x3F];
            *pcWrite++ = gs_szBin2Ascii[((u8_BytesArry[1] << 2) | (u8_BytesArry[2] >> 6)) & 0x3F];
            *pcWrite++ = gs_szBin2Ascii[u8_BytesArry[2] & 0x3F];
        }
        else
        {
            u8_BytesArry[0] = data[i];
            if(iLenIn - i == 2) u8_BytesArry[1] = data[i + 1];
            else u8_BytesArry[1] = 0;

            *pcWrite++ = gs_szBin2Ascii[u8_BytesArry[0] >> 2];
            *pcWrite++ = gs_szBin2Ascii[((u8_BytesArry[0] << 4) | (u8_BytesArry[1] >> 4)) & 0x3F];
            *pcWrite++ = (iLenIn - i == 1) ? '=' : gs_szBin2Ascii[(u8_BytesArry[1] << 2) & 0x3F];
            *pcWrite++ = '=';
        }
    } // End for i

    iRet = 0;
_Exit:
    return iRet;
}

int BASE64_Decode(const char* data, int iLenIn, OUT BYTE* pOut, int& iLenOut)
{
    int i, j, iRet = -1;
    U8 u8_BytesArry[4];
    BYTE* pWrite = pOut;
    char cTemp;

    if(data == NULL || iLenIn <= 0 || pOut == NULL || iLenOut <= 0) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);

    for(i = 0; i < iLenIn; i += 4)
    {
        for(j = 0; j < 4; j++)
        {
            cTemp = data[i + j];
            if((cTemp < 0) || (cTemp >= 0x80)) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);
            else
            {
                if(cTemp == '=') // 发现BASE64编码中的填充字符
                {
                    if(i + j == iLenIn - 1) break;
                    if(i + j != iLenIn - 2) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);
                    if(data[i + j + 1] == '=') break;
                    else SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);
                }
                else
                {
                    u8_BytesArry[j] = gs_u8aAscii2Bin[cTemp];
                    if(u8_BytesArry[j] & 0x80) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS); // 数据错误，无效的Base64编码字符
                }
            }
        } // End for j

        if(pWrite + j - 1 > pOut + iLenOut) SET_RET_AND_GOTO_EXIT(COMMON_ERR_NOMEM);

        if(j == 4)
        {
            *pWrite++ = (u8_BytesArry[0] << 2) | (u8_BytesArry[1] >> 4);
            *pWrite++ = (u8_BytesArry[1] << 4) | (u8_BytesArry[2] >> 2);
            *pWrite++ = (u8_BytesArry[2] << 6) | u8_BytesArry[3];
        }
        else if(j == 3)
        { // 有1个填充字节
            *pWrite++ = (u8_BytesArry[0] << 2) | (u8_BytesArry[1] >> 4);
            *pWrite++ = (u8_BytesArry[1] << 4) | (u8_BytesArry[2] >> 2);
            break;
        }
        else if(j == 2)
        { // 有2个填充字节
            *pWrite++ = (u8_BytesArry[0] << 2) | (u8_BytesArry[1] >> 4);
            break;
        }
        else SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS); // 数据错误，无效的Base64编码字符
    } // End for i

    iLenOut = pWrite - pOut;
    iRet    = 0;
_Exit:
    return iRet;
}
